package br.edu.ufcg.splab.util.commands;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.naturalcli.Command;
import org.naturalcli.ExecutionException;
import org.naturalcli.ICommandExecutor;
import org.naturalcli.IParameterType;
import org.naturalcli.InvalidSyntaxException;
import org.naturalcli.NaturalCLI;
import org.naturalcli.ParameterValidator;
import org.naturalcli.ParseResult;
import org.naturalcli.commands.HelpCommand;
import org.naturalcli.parameters.FileParamType;
import org.naturalcli.parameters.StringParamType;

import br.edu.ufcg.splab.util.Facade;


public class CommandParser {
	
	private Set<Command> commands;
	private List<IParameterType> parameters;
	
	public CommandParser(Facade facade) throws InvalidSyntaxException {
		this.commands = new HashSet<Command>();
		this.parameters = new ArrayList<IParameterType>();
		
		this.parameters.add(new CoverageCriteriaParameter());
		this.parameters.add(new GenerationAlgorithmParameter());
		this.parameters.add(new PositiveIntegerParameter());
		this.parameters.add(new StringParamType());
		this.parameters.add(new FileParamType());
		this.parameters.add(new ValidFilePathParameter());
		this.parameters.add(new PercentParameter());
		
		this.commands.add(this.defineGenerateCommand(facade));
		this.commands.add(this.defineGenerateAnnotatedCommand(facade));
		this.commands.add(this.defineSelectCommand(facade));
		this.commands.add(this.defineReduceCommand(facade));
		this.commands.add(this.defineReduceAnnotatedCommand(facade));
		this.commands.add(this.defineSARTCommand(facade));
		this.commands.add(new HelpCommand(this.commands));
	}
	
	/**
	 * Defines the command to generate test cases.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineGenerateCommand(final Facade facade) throws InvalidSyntaxException{
		Command generateCommand = new Command("generate <algorithm:generationalgorithm> [<folds:positiveinteger>] <inputfile:file> <outputfile:validfilepath>", 
											  "Generate test cases from the input file, representing a general LTS, and store them in the output file. The \'folds\' parameter is needed just with the " +
											  "\'parameterizedDFS\' algorithm.", new ICommandExecutor() {
												public void execute(ParseResult result) throws ExecutionException {
													String algorithm = result.getParameterValue(0).toString();
													Object folds = result.getParameterValue(1);
													String inputFile = result.getParameterValue(2).toString();
													String outputFile = result.getParameterValue(3).toString();
														try {
															if(algorithm.equals("DFS")){
																facade.generateFromGraph(inputFile, outputFile);
															}else if(algorithm.equals("parameterizedDFS") && folds != null){
																facade.generateFromTree(inputFile, Integer.valueOf(folds.toString()), outputFile);
															}else{
																System.out.println("Empty number of folds. Provide a positive integer.");
															}
														} catch (Exception e) {
															System.out.println(e.getMessage());
														}
												}
											});
		return generateCommand;
	}
	
	/**
	 * Defines the command to generate test cases, through a annotated LTS.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineGenerateAnnotatedCommand(final Facade facade) throws InvalidSyntaxException{
		Command generateAnnotatedCommand = new Command("generate_annotated <algorithm:generationalgorithm> [<folds:positiveinteger>] <inputfile:file> <outputfile:validfilepath>", 
											  "Generate test cases from the input file, representing an annotated LTS, and store them in the output file. The \'folds\' parameter is needed just with the " +
											  "\'parameterizedDFS\' algorithm.", new ICommandExecutor() {
												public void execute(ParseResult result) throws ExecutionException {
													String algorithm = result.getParameterValue(0).toString();
													Object folds = result.getParameterValue(1);
													String inputFile = result.getParameterValue(2).toString();
													String outputFile = result.getParameterValue(3).toString();
														try {
															if(algorithm.equals("DFS")){
																facade.generateAnnotatedGraphFromGraph(inputFile, outputFile);
															}else if(algorithm.equals("parameterizedDFS") && folds != null){
																facade.generateAnnotatedGraphFromTree(inputFile, Integer.valueOf(folds.toString()), outputFile);
															}else{
																System.out.println("Empty number of folds. Provide a positive integer.");
															}
														} catch (Exception e) {
															System.out.println(e.getMessage());
														}
												}
											});
		return generateAnnotatedCommand;
	}
	
	/**
	 * Defines the command to select test cases, through the similarity algorithm.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineSelectCommand(final Facade facade) throws InvalidSyntaxException {
		Command selectCommand = new Command("select <percentselection:percent0and1> <inputfile:file> <outputfile:validfilepath>", 
				  "Select test cases from the input file, according to the percentage of selection, and store the resultant test suite in the output file.", 
				  new ICommandExecutor() {
					public void execute(ParseResult result) throws ExecutionException {
						Object percentage = result.getParameterValue(0);
						String inputFile = result.getParameterValue(1).toString();
						String outputFile = result.getParameterValue(2).toString();
						
						try {
							facade.similaridade(inputFile, Double.valueOf(percentage.toString()), outputFile);
						} catch (Exception e) {
							System.out.println(e.getMessage());
						}	
					}
				});
		return selectCommand;
	}
	
	
	/**
	 * Defines the command to reduce test suite based on similarity.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineReduceCommand(final Facade facade) throws InvalidSyntaxException{
		Command reduceCommand = new Command("reduce <criteria:coveragecriteria> <algorithm:generationalgorithm> [<folds:positiveinteger>] <inputfile:file> <outputfile:validfilepath>", 
											  "Reduce test suite from the input file, representing a general LTS, and store them in the output file. The \'folds\' parameter is needed just with the " +
											  "\'parameterizedDFS\' algorithm.", new ICommandExecutor() {
												public void execute(ParseResult result) throws ExecutionException {
													String criteria = result.getParameterValue(0).toString();
													String algorithm = result.getParameterValue(1).toString();													
													Object folds = result.getParameterValue(2);
													String inputFile = result.getParameterValue(3).toString();
													String outputFile = result.getParameterValue(4).toString();
														try {
															if(criteria.equals("T") || criteria.equals("P") || criteria.equals("B")){
																if(algorithm.equals("DFS")){											
																	facade.reduceSimFromGraphToGraph(inputFile, outputFile, criteria);
																}else if(algorithm.equals("parameterizedDFS") && folds != null){
																	facade.reduceSimFromGraphToTree(inputFile, Integer.valueOf(folds.toString()), outputFile, criteria);
																}else{
																	System.out.println("Empty number of folds. Provide a positive integer.");
																}
															}else{
																System.out.println("Coverage criterion invalid. Use: T = all-transitions; P = all-transition-pairs and B = bi-criteria.");
															}
														} catch (Exception e) {
															System.out.println(e.getMessage());
														}
												}
											});
		return reduceCommand;
	}
	
	/**
	 * Defines the command to reduce test suite based on similarity, through a annotated LTS.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineReduceAnnotatedCommand(final Facade facade) throws InvalidSyntaxException{
		Command reduceAnnotatedCommand = new Command("reduce_annotated <criteria:coveragecriteria> <algorithm:generationalgorithm> [<folds:positiveinteger>] <inputfile:file> <outputfile:validfilepath>", 
											  "Reduce test suite from the input file, representing an annotated LTS, and store them in the output file. The \'folds\' parameter is needed just with the " +
											  "\'parameterizedDFS\' algorithm.", new ICommandExecutor() {
												public void execute(ParseResult result) throws ExecutionException {
													String criteria = result.getParameterValue(0).toString();
													String algorithm = result.getParameterValue(1).toString();													
													Object folds = result.getParameterValue(2);
													String inputFile = result.getParameterValue(3).toString();
													String outputFile = result.getParameterValue(4).toString();					
													try {
														if(criteria.equals("T") || criteria.equals("P") || criteria.equals("B")){
															if(algorithm.equals("DFS")){
																facade.reduceSimFromAnnotatedGraphToGraph(inputFile, outputFile, criteria);
															}else if(algorithm.equals("parameterizedDFS") && folds != null){
																facade.reduceSimFromAnnotatedGraphToTree(inputFile, Integer.valueOf(folds.toString()), outputFile, criteria);
															}else{
																System.out.println("Empty number of folds. Provide a positive integer.");
															}
														}else{
															System.out.println("Coverage criterion invalid. Use: T = all-transitions; P = all-transition-pairs and B = bi-criteria.");
														}
													} catch (Exception e) {
														System.out.println(e.getMessage());
													}
												}
											});
		return reduceAnnotatedCommand;
	}
	
	/**
	 * Defines the command to reduce test suite based on similarity, through a annotated LTS.
	 * @param facade The system facade.
	 * @return The command, equipped with the calls to the facade. 
	 * @throws InvalidSyntaxException 
	 */
	private Command defineSARTCommand(final Facade facade) throws InvalidSyntaxException{
		Command sartCommand = new Command("select_sart <inputfile1:file> <inputfile2:file> <outputfile:validfilepath>", 
											  "Selects test cases using the SART approach, based on the specified baseline and delta test suites.", new ICommandExecutor() {
												public void execute(ParseResult result) throws ExecutionException {
													String inputFile1 = result.getParameterValue(0).toString();
													String inputFile2 = result.getParameterValue(1).toString();
													String outputFile = result.getParameterValue(2).toString();
														try {
															facade.selectSART(inputFile1, inputFile2, outputFile);											
															
														} catch (Exception e) {
															System.out.println(e.getMessage());
														}
												}
											});
		return sartCommand;
	}
	
	
	public void execute(String[] args) throws ExecutionException {
		new NaturalCLI(this.commands, new ParameterValidator(this.parameters)).execute(args);
	}
}
